package com.google.code.netcheckin.xastir;

import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;

import com.google.code.netcheckin.aprs.AprsFormatter;
import com.google.code.netcheckin.aprs.AprsPacketDataObject;
import com.google.code.netcheckin.aprs.LatOrLong;
import com.google.code.netcheckin.aprs.Station;

/**
  * An object that can be used to transmit APRS objects through Xastir.
  * Instantiate, use accessor methods to configure, pass a Station object
  * to xmit.
  * @author Dick Repasky
  *
*/

public class ObjectXmitter {

	protected byte[] senderCall;
	protected byte[] path = new byte[0];
	protected byte[] destAddress = "APRS".getBytes();
	protected boolean xmitRf = false;
	protected boolean xmitIgate = false;

		/** General comment for all outgoing packets. Takes
 		    precedence over comments in Station objects
		    when not null. To force no comment in all packets,
		    set this field to "". */
	protected byte[] comment;

	protected AprsPacketDataObject objectData;

	protected XastirUdpClient xmitter;

	/** xastirLogin is also the call sign of the sender that is placed in 
  		packets. */

	public ObjectXmitter(String xastirHost, int xastirPort, 
			String xastirLogin, String xastirPassword) 
				throws UnknownHostException, SocketException {
		senderCall = xastirLogin.getBytes();
		xmitter = new XastirUdpClient(xastirHost, xastirPort, 
				xastirLogin, xastirPassword);
		objectData = new AprsPacketDataObject(new AprsFormatter());
	}

	public final void close() throws IOException {
		xmitter.close();
	}

	public final byte[] getAddressee() {
		return destAddress;
	}

	public final byte[] getComment() {
		return comment;
	}

	public final byte[] getPath() {
		return path;
	}

		/**
 	          * Timeout when waiting for acknowledgement from xastir.
 	          * @return timeout in milliseconds.
 	          *
 	        */

	public final int getSoTimeout() throws SocketException {
		return xmitter.getSoTimeout();
	}

	public final boolean getXmitIgate() {
		return xmitIgate;
	}

	public final boolean getXmitRf() {
		return xmitRf;
	}

		/** Use this if you want an destAddress other than "APRS". */
	public final void setAddressee(String destAddress) {
		this.destAddress = destAddress.getBytes();
	}

		/** Use this to set a standard comment for all packets sent. 
 		    Takes precedence over comments in Station objects when
		    not null.  Set to "" to forbid any comment in all 
		    packets. */
	public final void setComment(String comment) {
		if (comment.length() > 43) {
			comment = comment.substring(0, 43);
		}
		this.comment = comment.getBytes();
	}

		/** Use this if you want a path other than "". 
		    Note that xastir ignores objects with paths other
		    than "". */

	public final void setPath(String path) {
		this.path = path.getBytes();
	}

		/**
  		  * Set timeout when waiting for acknowledgment from xastir.
  		  * Default value XastirUdpClient.SOCKET_TIMEOUT_MILLIS.
  		  * @param millisec - timeout in milliseconds.
  		  *
  		*/

	public final void setSoTimeout(int millisec) throws SocketException {
		xmitter.setSoTimeout(millisec);
	}

		/** Use this if you would like to have xastir xmit objects on the network. The defaul is not. */

	public final void setXmitIgate(boolean to) {
		xmitIgate = to;
	}

		/** Use this if you would like to have xastir xmit objects over RF. The defaul is not. */

	public final void setXmitRf(boolean to) {
		xmitRf = to;
	}

		/**
		  * Deprecated. Use the xmit method that includes a type
		  * argument.  This method will be removed as soon as
		  * calls to it have been removed from the code.
		  *
		  **/

	public synchronized final int xmit(Station station, boolean usePrimary) 
				throws IOException {
		return xmit(station, usePrimary, AprsPacketDataObject.OBJECT_CREATE);
	}

		/**
		  * Transmit a packet that either creates or kills an object
		  * for a station, depending on the type argument. 
		  * @param type - use either AprsPacketDataObject.OBJECT_CREATE
		  * or AprsPacketDataObject.OBJECT_KILL.
		  * @return XastirUdpClient.ACK XastirUdpClient.NACK.
		  * @throws java.io.IOException. You can detect 
		  * timeouts when waiting for a response from xastir
		  * by testing the exception to see if it is
		  * a java.net.SocketTimeoutException.
		  *
		  **/

	public synchronized final int xmit(Station station, boolean usePrimary,
				byte type) throws IOException {
		configureObject(station, usePrimary, type);
		byte[] packet = objectData.toPacket();
		xmitter.setToXastirIgate(xmitIgate);
		xmitter.setToXastirRf(xmitRf);
		return xmitter.send(packet);
	}

	///////////// protected below //////////////////////

		/**
 		  * @param type - use either AprsPacketDataObject.OBJECT_CREATE
 		  * or AprsPacketDataObject.OBJECT_KILL.
 		  *
 		  */

	protected void configureObject(Station station, boolean usePrimary,
				byte type) {
		objectData.setSourceAddress(senderCall);
		objectData.setDestAddress(destAddress);
		objectData.setPath(path);
		if (comment != null) {
			objectData.setComment(comment);
		} else {
			objectData.setComment(station.getComment());
		}
		objectData.setObjectName(station.getCallsign().getBytes());
		objectData.setTimestamp(System.currentTimeMillis());
		objectData.setObjectStatus(type);
		if (usePrimary) {
			objectData.setSymbolTable(station.getPrimaryIcon().getBytes()[0]);
			objectData.setSymbolId(station.getPrimaryIcon().getBytes()[1]);
			objectData.setLatitude(new LatOrLong(station.getPrimaryLatitude1(),
					new Float(station.getPrimaryLatitude2()).doubleValue(),
					new Character(station.getPrimaryLatitudeOrientation()).toString().getBytes()[0],
					0));
			objectData.setLongitude(new LatOrLong(station.getPrimaryLongitude1(),
					new Float(station.getPrimaryLongitude2()).doubleValue(),
					new Character(station.getPrimaryLongitudeOrientation()).toString().getBytes()[0],
					0));
		} else {
			objectData.setSymbolTable(station.getSecondaryIcon().getBytes()[0]);
			objectData.setSymbolId(station.getSecondaryIcon().getBytes()[1]);
			objectData.setLatitude(new LatOrLong(station.getSecondaryLatitude1(),
					new Float(station.getSecondaryLatitude2()).doubleValue(),
					new Character(station.getSecondaryLatitudeOrientation()).toString().getBytes()[0],
					0));
			objectData.setLongitude(new LatOrLong(station.getSecondaryLongitude1(),
					new Float(station.getSecondaryLongitude2()).doubleValue(),
					new Character(station.getSecondaryLongitudeOrientation()).toString().getBytes()[0],
					0));
		}
	}

}
